/**
 * @namespace
 */
Antiquity.WPL = {};
/**
 * Create a popup in the page.
 * @author yaoxing zhang
 * @class This class provide popup functionalities in the web pages.
 * @constructor
 * @param {DOMElement} popupElm Necessary. Popup DOM element.
 * @param {Object} options Optional. Popup options which control popup behavior.
 * @param {DOMElement} anchorElm Optional. Anchor element.
 * @memberOf Antiquity.WPL
 */
Antiquity.WPL.Popup = function() {
	this.state = Antiquity.WPL.State.INIT;
	var anchorElm, popupElm;
	var options = {};
	switch(arguments.length) {
	case 3:
		anchorElm = $(arguments[2]);
	case 2:
		options = $(arguments[1]);
	case 1:
		popupElm = $(arguments[0]);
		break;
	default:
		throw new Error("Only 1~3 parameters are accepted!");
		break;
	}
	this.popupElm = popupElm;
	this.visibleStack = 0;
	this.isVisible = false;
	this.showId = 0;
	this.hideId = 0;
	this.anchorElms = [];
	
	// fill default options
	// define a group of default options
	var ns = Antiquity.WPL;
	var defaultOptions = {
		// specify an offset for popup window;
		offset: {
			x: 0,
			y: 0
		},
		anchor: {
			// anchor point of anchor object
			target: ns.Anchor.BOTTOM_RIGHT,	
			// anchor point of popup window
			popup: ns.Anchor.TOP_LEFT
		},
		alwaysUpdate: false,	// always update the position and size
		// TODO: deferred
		//container: Antiquity.Bridge.getOffsetParent(options.anchor.object),	// popup window container
		showDelay: 0.1,	// delay by seconds
		hideDelay: 0.1,	// delay by seconds
		type: ns.Type.NORMAL,	// popup type
		showEvent: "mouseover",	// the event by which the popup will be shown
		hideEvent: "mouseout"	// the event by which the popup will be hidden
	};
	this.options = this._fillDefault(null, defaultOptions, options);
	
	if (this.options.type == ns.Type.NORMAL) {
		if (this.options.showEvent == "mouseover") {
			Antiquity.Bridge.observe(this.popupElm, this.options.showEvent, this._delayedShow.bind(this));
		}
		if (this.options.hideEvent == "mouseout") {
			Antiquity.Bridge.observe(this.popupElm, this.options.hideEvent, this._delayedHide.bind(this));
		}
	} else if (this.options.type == ns.Type.DIALOG) {
		this.state = Antiquity.WPL.State.RUNNING;
	}
	this._initPopup();
	if (anchorElm) {
		this.attachTo(anchorElm);
	}
};
Antiquity.WPL.Popup.prototype = Antiquity.Bridge.extendObject(new Antiquity.EventDriven(), {
	/**
	 * Anchor DOM objects by operating which the user can show or hide the popup.
	 * @see Antiquity.WPL.Popup#popup
	 * @private
	 */
	anchorElms: null,
	/**
	 * Popup DOM object.
	 * @see Antiquity.WPL.Popup#anchor
	 * @private
	 */
	popupElm: null,
	/**
	 * Popup element inside
	 * @private
	 */
	innerPopup: null,
	/**
	 * Options that customize the popup behavior.
	 * @private
	 */
	options : null,
	/**
	 * Indicate current status of popup window.
	 * <b>IMPORTANT</b> for type <i>NORMAL</i> popup. <br />
	 * Normally type <i>NORMAL</i> popup should be shown when the showing event is fired, 
	 * and hidden when the hiding event is fired. However, <i>mouseover</i> event is a special one.
	 * Usually user would expect the popup not to be hidden when moving from the anchor object 
	 * to the popup. Plus in some situations <i>mouseover</i> event bubbles up from inner elements, 
	 * I introduce a stack to solve this problem. This stack will <b>ONLY</b> work when using <i>mouseover</i> 
	 * and <i>mouseout</i> together at the same time. When <i>mouseover</i> event is fired, this stack pluses 1; 
	 * When <i>mouseout</i> event is fired, this stack minus 1. Finally, if the stack is greater than 0, 
	 * the popup would be shown. Otherwise it would be hidden.
	 * @private
	 */
	visibleStack: 0,
	
	/**
	 * options that used for different anchor elements to override default options.
	 */
	overrideOptions : null,
	
	/**
	 * Indicates whether the popup is visible or not
	 * @private
	 */
	isVisible: false,
	/**
	 * Program would start after this flag being set to true. This will avoid some bugs.
	 * @private
	 */
	startFlag: false,
	/**
	 * ID of timeout event for hiding popup.
	 * @private
	 */
	hideId: -1,
	/**
	 * ID of timeout event for showing popup.
	 * @private
	 */
	showId: -1,
	/**
	 * State of this popup.
	 */
	state: null,
	_delayedShow: function(e) {
	},
	_delayedHide: function(e) {
	},
	_showLogic: function() {
	},
	_hideLogic: function() {
	},
	show: function() {
	},
	hide: function() {
	},
	attachTo: function(element, options) {
		if (this.options.type == Antiquity.WPL.Type.DIALOG) {
			// DIALOG popup needs no anchor element
			throw new Error("Unnecessary anchor element attached!");
		}
		// bind events
		var showEvent = this.options.showEvent;
		var hideEvent = this.options.hideEvent;
		
		this.anchorElms.push(element);
		if (showEvent === hideEvent && showEvent !== null) {
			// for the situation when the show event and hide event are the same. (e.g. "click")
			Antiquity.Bridge.observe(element, showEvent, function(e) {
				if (this.isVisible) {
					this._delayedHide(e);
				} else {
					this._delayedShow(e);
				}
			}.bind(this));
		} else {
			if (showEvent !== null) {
				Antiquity.Bridge.observe(element, showEvent, this._delayedShow.bind(this));
			}
			if (hideEvent !== null) {
				Antiquity.Bridge.observe(element, hideEvent, this._delayedHide.bind(this));
			}
		}
		var tempOpt = Antiquity.Bridge.extend({}, this.options);
		this.overrideOptions.push(Antiquity.Bridge.extend(tempOpt, options));
	},
	/**
	 * Uninstall this popup, release resources
	 */
	uninstall: function() {
		if (this.state != Antiquity.WPL.State.DESTROYED) {
			this.anchorElms.clear();
			Antiquity.Bridge.remove(this.popupElm);
			this.popupElm = null;
			this.state = Antiquity.WPL.State.DESTROYED;
		}
	},
	/**
	 * Initialize popup DOM element.
	 */
	_initPopup: function() {
	},
	/**
	 * This function will fill default options to user defined options.
	 * So that the options object would contain all the necessary options. 
	 * @param {DOMElement} anchor Anchor object
	 * @param {Object} defaultOptions default options
	 * @param {Object} options User defined options
	 */
	_fillDefault: function(anchor, defaultOptions, options) {
		var newOptions = Antiquity.Bridge.extendObject({}, defaultOptions);
		options.offset = Antiquity.Bridge.extendObject(newOptions.offset, options.offset);
		// specify anchor information
		options.anchor = Antiquity.Bridge.extendObject(newOptions.anchor, options.anchor);
		// other options
		this.options = Antiquity.Bridge.extendObject({
			
		}, options);
	}
});

/**
 * Anchor constants.
 * TOP : 1,
 * LEFT : 2,
 * BOTTOM : 4,
 * RIGHT : 8,
 * H_CENTER : 16,
 * V_CENTER : 32,
 * TOP_LEFT: 3,
 * TOP_RIGHT: 9,
 * BOTTOM_LEFT: 6,
 * BOTTOM_RIGHT: 12,
 * CENTER: 48
 * @memberOf Antiquity.WPL
 */
Antiquity.WPL.Anchor = {
	TOP : 1,
	LEFT : 2,
	BOTTOM : 4,
	RIGHT : 8,
	H_CENTER : 16,
	V_CENTER : 32,
	TOP_LEFT: 3,
	TOP_RIGHT: 9,
	BOTTOM_LEFT: 6,
	BOTTOM_RIGHT: 12,
	CENTER: 48
};
/**
 * Popup type.
 * MODAL: Simulate a modal dialog by using which you can prevent any operation
 * 	to the elements out side the popup window.<br />
 * FOLLOW: A mouse follow popup window.<br />
 * NORMAL: Normal popup window which has a fixed position. The position is customizable.<br />
 * @memberOf Antiquity.WPL
 */
Antiquity.WPL.Type = {
	DIALOG: "dialog",
	NORMAL: "normal"
};
/**
 * Popup events.
 * GENERATING: Fires before popup is generated.
 * GENERATED: Fires after popup is generated.
 * SHOWING: Fires before popup is shown.
 * SHOWN: Fires after popup is shown.
 * HIDING: Fires before popup is hidden.
 * HIDDEN: Fires after popup is hidden.
 * @memberOf Antiquity.WPL
 */
Antiquity.WPL.Events = {
	GENERATING: "generating",
	GENERATED: "generated",
	SHOWING: "showing",
	SHOWN: "shown",
	HIDING: "hiding",
	HIDDEN: "hidden"
};
/**
 * State of popup.
 * @memberOf Antiquity.WPL
 */
Antiquity.WPL.State = {
	INIT: "init",
	HIDDEN: "hidden",
	DESTROYED: "destroyed"
};
Antiquity.WPL.Popup.zIndex = 1000;